import os
# 从SCons库导入环境类，用于创建和配置构建环境
from SCons.Environment import Environment

# ----------------------- 用户配置区域 -----------------------
# 构建输出目录：所有编译生成的文件（目标文件、可执行文件等）将存放于此
BUILD_DIR = 'Build_SCons'
# 目标文件名：生成的可执行文件和固件文件的基础名称
TARGET = "AIR32Lab"
# 编译优化级别：-Ofast表示最高级别的优化（速度优先）
OPTIMIZE = '-Ofast'
# 目标MCU型号：指定使用的微控制器型号为AIR32F103CC
MCU = "AIR32F103CC"
# 架构参数：指定CPU架构为Cortex-M3
ARCH = "-mcpu=cortex-m3"
# 链接脚本路径：用于指定程序在Flash中的存储布局和内存分配
LINKER_SCRIPT = "Libs/STARTUP/gcc/linker/AIR32F103XB_FLASH.ld"
# ARM工具链路径：GNU Arm嵌入式工具链的安装路径
ARM_TOOLCHAIN_PATH = "C:/Program Files (x86)/GNU Arm Embedded Toolchain/10 2021.10/bin"

# pyOCD配置（用于固件下载）
# 设备支持包路径：包含AIR32F103芯片的调试信息
PYOCD_PACK = "Libs/Keil.AIR32F103_DFP.1.1.9.pack"
# 下载频率：调试器与目标芯片通信的频率（1M表示1MHz）
PYOCD_FREQ = "1M"

# ----------------------- 构建环境配置 -----------------------
# 创建SCons环境对象，指定使用的工具链（gcc、g++、gnulink）
# tools 参数用于指定构建环境中加载的内置工具模块，这些工具封装了特定编译器、
# 链接器的行为逻辑，是 SCons 实现跨平台、跨工具链构建的核心机制。
# ENV=os.environ.copy() 表示复制当前系统的环境变量，确保工具链可被找到
env = Environment(
    tools=['gcc', 'g++', 'gnulink'],
    ENV=os.environ.copy()  # 继承系统环境变量（如PATH）
)

# 将ARM工具链路径添加到环境变量PATH中，确保SCons能找到编译工具
# 使用分号分隔路径（Windows系统），避免覆盖原有PATH配置
env['ENV']['PATH'] += ';' + ARM_TOOLCHAIN_PATH

# 替换环境中的工具变量，指定使用ARM交叉编译工具链
env.Replace(
    CC      = 'arm-none-eabi-gcc',      # C编译器：ARM架构专用的GCC
    CXX     = 'arm-none-eabi-g++',      # C++编译器：ARM架构专用的G++
    AS      = 'arm-none-eabi-as',       # 汇编器：ARM架构专用的汇编工具
    AR      = 'arm-none-eabi-ar',       # 归档器：用于创建静态库
    OBJCOPY = 'arm-none-eabi-objcopy',  # 目标文件转换器：用于生成HEX/BIN等格式
    OBJDUMP = 'arm-none-eabi-objdump',  # 目标文件反汇编器：用于查看二进制文件内容
    SIZE    = 'arm-none-eabi-size'      # 大小分析工具：用于查看程序各段大小
)

# ----------------------- 编译参数配置 -----------------------
# 向环境中添加编译、链接等参数
env.Append(
    # C编译器参数（CCFLAGS）：适用于C文件编译
    CCFLAGS = [
        ARCH,                   # 指定CPU架构（Cortex-M3）
        OPTIMIZE,               # 编译优化级别（-Ofast）
        '-mthumb',              # 启用Thumb指令集（ARM Cortex-M系列专用）
        '-Wall',                # 开启所有警告信息（提高代码质量）
        '-ffunction-sections',  # 将每个函数编译到独立的段中（便于链接时剔除未使用函数）
        '-fdata-sections',      # 将每个数据项编译到独立的段中（便于链接时剔除未使用数据）
        '-fno-common',          # 禁止将未初始化的全局变量放入common段（避免符号冲突）
    ],
    
    # 汇编器参数（ASFLAGS）：适用于汇编文件编译
    ASFLAGS = [
        ARCH,                   # 同CCFLAGS，指定CPU架构
        OPTIMIZE,               # 汇编优化
        '-mthumb',              # 启用Thumb指令集
        '-Wall',                # 开启汇编警告
        '-ffunction-sections',  # 同CCFLAGS，函数独立分段
        '-fdata-sections',      # 同CCFLAGS，数据独立分段
    ],
    
    # 链接器参数（LINKFLAGS）：适用于链接阶段
    LINKFLAGS = [
        ARCH,                       # 指定CPU架构
        '-mthumb',                  # 启用Thumb指令集
        '-specs=nano.specs',        # 使用newlib-nano库（减小代码体积，适合嵌入式系统）
        '-specs=nosys.specs',       # 不使用系统调用（嵌入式系统通常无操作系统）
        '-Wl,--gc-sections',        # 链接时移除未使用的段（减小最终程序体积）
        f'-T{LINKER_SCRIPT}'        # 指定链接脚本（-T参数）
    ],
    
    # 预定义宏（CPPDEFINES）：编译时定义的宏，相当于代码中的#define
    CPPDEFINES = [
        MCU,             # 定义MCU型号（如AIR32F103CC，用于条件编译）
        'RELEASE'        # 定义RELEASE宏（可用于区分调试/发布版本）
    ],
    
    # 头文件搜索路径（CPPPATH）：编译器查找#include文件的路径
    CPPPATH = [
        'Src',                                  # 项目源代码目录
        'Libs/CMSIS/Include',                   # CMSIS标准库头文件
        'Libs/AIR32F10xLib/inc',                # AIR32F10x系列外设库头文件
        'Libs/AIR32F10xLib/lib/cryptlib',       # 加密库头文件
        'Libs/AIR32_USB-FS-Device_Driver/inc'   # USB设备驱动头文件
    ],
    
    # 链接库（LIBS）：需要链接的系统库
    LIBS = ['c', 'm', 'nosys']  # c：C标准库；m：数学库；nosys：空的系统调用库（适配nosys.specs）
)

# ----------------------- 源文件收集 -----------------------
# 项目所有源文件列表：包含C文件和汇编文件（.c和.s）
sources =  [
    # AIR32F10x外设库源文件
    'Libs/AIR32F10xLib/src/misc.c',
    'Libs/AIR32F10xLib/src/air32f10x_rcc.c',          # 时钟控制
    'Libs/AIR32F10xLib/src/air32f10x_rcc_ex.c',      # 扩展时钟控制
    'Libs/AIR32F10xLib/src/air32f10x_gpio.c',         # GPIO外设
    'Libs/AIR32F10xLib/src/air32f10x_usart.c',        # USART串口
    'Libs/AIR32F10xLib/src/air32f10x_spi.c',          # SPI通信
    'Libs/AIR32F10xLib/src/air32f10x_tim.c',          # 定时器
    'Libs/AIR32F10xLib/src/air32f10x_dma.c',          # DMA传输
    'Libs/AIR32F10xLib/src/air32f10x_dac.c',          # DAC数模转换
    'Libs/AIR32F10xLib/src/system_air32f10x.c',       # 系统初始化
    
    # 项目应用源代码
    'Src/air32f10x_it.c',    # 中断服务程序
    'Src/sys.c',             # 系统相关配置
    'Src/usart1.c',          # USART1驱动
    'Src/spi1.c',            # SPI1驱动
    'Src/w25qxx.c',          # W25Q系列Flash芯片驱动
    'Src/printf.c',          # 重定向printf函数
    'Src/led.c',             # LED控制
    'Src/font.c',            # 字体相关
    'Src/dac1.c',            # DAC1相关功能
    'Src/tim2.c',            # 定时器2应用
    'Src/tim3.c',            # 定时器3应用
    'Src/tim4.c',            # 定时器4应用
    'Src/tim5.c',            # 定时器5应用
    'Src/curtain.c',         # 窗帘控制功能
    'Src/volleyball.c',      # 排球相关功能
    'Src/5m3x.c',            # 5m3x模块功能
    'Src/old5m3x.c',         # 旧版5m3x模块兼容代码
    'Src/main.c',            # 主程序入口
    
    # 启动文件（汇编）：芯片复位后第一个执行的代码，负责初始化堆栈和跳转到main
    'Libs/STARTUP/gcc/startup_air32f10x_big.s'
]

# ----------------------- 构建目录配置 -----------------------
# VariantDir：将源文件的编译输出重定向到BUILD_DIR目录
# 参数说明：
#   BUILD_DIR：目标目录（存放编译产物）
#   '.'：源文件根目录
#   duplicate=False：不复制源文件到目标目录（避免冗余）
env.VariantDir(BUILD_DIR, '.', duplicate=False)

# 将源文件路径转换为BUILD_DIR下的路径（因为编译输出已重定向）
sources = [os.path.join(BUILD_DIR, src) for src in sources]

# ----------------------- 目标构建 -----------------------
# 构建ELF格式可执行文件：将所有源文件编译链接为一个ELF文件
# Program是SCons的内置构建器，用于生成可执行程序
elf = env.Program(  
    target = f'{BUILD_DIR}/{TARGET}.elf',  # 目标文件路径和名称
    source = sources                       # 依赖的源文件列表
)

# 生成HEX格式固件文件：从ELF文件转换为HEX格式（适合烧录到芯片）
# Command是SCons的自定义命令构建器，用于执行外部命令
hex_file = env.Command(
    target = f'{BUILD_DIR}/{TARGET}.hex',  # 目标HEX文件路径
    source = elf,                          # 依赖的ELF文件
    # 执行objcopy命令：将ELF转换为Intel HEX格式
    action = f'arm-none-eabi-objcopy -O ihex $SOURCE $TARGET'
)

# ----------------------- 下载功能配置 -----------------------
# 定义pyOCD下载命令：通过pyOCD工具将HEX文件烧录到目标芯片
download_command = (
    f'pyocd flash '                  # pyOCD的烧录命令
    f'-t {MCU} '                     # 指定目标芯片型号
    f'--pack {PYOCD_PACK} '          # 指定设备支持包
    f'-f {PYOCD_FREQ} '              # 指定下载频率
    f'{BUILD_DIR}/{TARGET}.hex'      # 要烧录的HEX文件路径
)

# 创建下载目标：
# 1. env.Alias：为下载操作创建一个名为'download'的别名
# 2. env.AlwaysBuild：标记该目标为"始终执行"（即使HEX文件未更新也会运行）
download_target = env.AlwaysBuild(
    env.Alias(
        'download',                  # 别名：执行`scons download`即可触发下载
        hex_file,                    # 依赖：必须先生成HEX文件才能下载
        # 执行下载命令，并在控制台显示命令信息
        Action(download_command, cmdstr="执行下载命令: $COMMAND")
    )
)

# 设置默认目标：执行`scons`时默认构建ELF文件
Default(elf)
